Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 31, 2025

JavaScript Runtime Implementation - Complete ✅

Summary

This PR implements a JavaScript language runtime API for app/terminal/, similar to the existing Python runtime (app/terminal/python/runtime.tsx). The implementation uses a web worker with eval() to execute code, and handles interruption by terminating and recreating the worker while restoring state through command replay.

Implementation Checklist

  • Create app/terminal/javascript/ directory structure
  • Create public/javascript.worker.js - Web worker for JavaScript execution with eval()
  • Create app/terminal/javascript/runtime.tsx - React context provider similar to Python runtime
  • Update public/_headers to add CORS headers for javascript.worker.js
  • Update app/terminal/runtime.tsx to include JavaScript runtime in RuntimeLang type and RuntimeProvider
  • Add JavaScript language constants and runtime mapping
  • Create app/terminal/javascript/page.tsx for testing the runtime
  • Update tests.ts to include JavaScript test cases
  • Update editor.tsx to support JavaScript syntax highlighting
  • Add JavaScript syntax highlighting support in highlight.ts
  • Fix worker to use direct eval() for proper variable persistence across commands
  • Address code review feedback (infinite recursion prevention, timeout handling)
  • Address PR feedback (payload types, command storage location, mutex usage)

Recent Changes (Latest Commit)

Fixed all PR review comments:

  1. Added empty payload?: undefined to init and updated restoreState to accept { commands: string[] }
  2. Changed initializeWorker() to return a Promise and wrapped interrupt() in mutex.runExclusive()
  3. Removed isInterrupted check - now relying on "Worker interrupted" error message
  4. Moved executedCommands storage from worker to runtime.tsx (survives worker termination)

Core Features

Web Worker-based JavaScript Runtime

  • Uses eval() to execute JavaScript code in a web worker
  • Maintains state across multiple REPL commands
  • Captures console output (console.log, console.error, etc.)
  • Returns expression values when defined
  • Prevents infinite recursion by using self.console in error handlers

Interrupt & State Recovery

  • Terminates worker on interrupt (stops infinite loops)
  • Saves successfully executed commands in runtime.tsx (persists across worker restarts)
  • Recreates worker and replays commands to restore state
  • Uses mutex to prevent race conditions during interrupt/restore
  • No SharedArrayBuffer needed (unlike Python's Pyodide)

UI Integration

  • JavaScript REPL terminal with syntax highlighting
  • JavaScript code editor with Ace editor support
  • Proper prompt (">") and language constants
  • Test page at /terminal/javascript

Test Coverage

  • Added JavaScript test cases to tests.ts for:
    • REPL stdout capture
    • Variable preservation across commands
    • Error handling
    • Interrupt and state recovery

Screenshots

JavaScript REPL and Editor

JavaScript Runtime Page

Security Summary

CodeQL Findings

CodeQL detected 2 code injection alerts in public/javascript.worker.js related to the use of eval():

  1. Line 37: eval(code) in runJavaScript function
  2. Line 78: eval(command) in restoreState function

Security Assessment: ✅ Accepted by Design

These alerts are expected and acceptable for the following reasons:

  1. By Design: The entire purpose of this runtime is to execute user-provided JavaScript code in a REPL environment, similar to how Python's runtime executes user code with Pyodide.

  2. Isolation: The code executes in a Web Worker, which provides isolation from the main page:

    • Worker runs in a separate global scope
    • No direct access to the DOM
    • Limited access to main thread resources
  3. Consistent with Existing Implementation: This matches the pattern of the Python runtime, which also executes arbitrary user code (via Pyodide's runPythonAsync).

  4. Expected Usage: Users are expected to run their own code in this environment - it's an educational/development tool, not a production code executor.

  5. No Additional Risk: The risk profile is no different from the existing Python runtime or any other code playground/REPL tool.

Mitigations in Place:

  • Executes in isolated Web Worker context
  • No file system access (file execution returns error)
  • Same security model as Python runtime
  • User is in control of what code they execute

注意点 (Implementation Notes)

  • JavaScriptでは実行中のコードを中断できないため、interrupt()が呼ばれるとworkerを停止して再度初期化します
  • 過去に成功したコマンドはruntime.tsx側に保存され、interrupt()から復帰した際にすべて再実行することで状態を復元します
  • ファイル実行機能はこのruntimeではサポートされていません(エラーメッセージを返します)
  • エラーハンドラーではself.consoleを使用して、console proxyとの無限再帰を防いでいます
Original prompt

app/terminal/ 内に、JavaScript言語のランタイムAPIを作ってください。pythonのランタイム(app/terminal/python/runtime.tsx)と同様にweb workerを作成し、worker内でeval()を使ってコードを実行する実装にしてください。

pythonのruntimeはinterrupt()をpyodideのAPIで実装していますが、javascriptではコードを中断することはできないので、interruptが呼ばれた場合にはworkerを停止して再度初期化する実装にしてください。また、runCommandが成功するたびにそのコマンドを保存しておく機能を追加し、interrupt()から復帰した際に保存した過去のコマンドを再度すべて実行することで元の状態に復元するような実装をしてください。

workerのjsファイルを作成する際には /public/_headers にもpyodideと同様にworkerの記述を追加する必要があります。


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Oct 31, 2025

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
my-code 35244c8 Commit Preview URL

Branch Preview URL
Oct 31 2025, 07:38 PM

Copilot AI changed the title [WIP] Add JavaScript runtime API with web worker implementation Add JavaScript runtime with eval-based execution and interrupt via worker termination Oct 31, 2025
Copilot AI requested a review from na-trium-144 October 31, 2025 08:25
const nextMessageId = useRef<number>(0);
const isInterrupted = useRef<boolean>(false);

function postMessage<T>({ type, payload }: MessageToWorker) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
error: Property 'payload' does not exist on type 'MessageToWorker'.

initとrestorestateメッセージに空のpayloadを追加すると解決すると思います。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdfa2a1. Added payload?: undefined to init type and payload: { commands: string[] } to restoreState type.

Comment on lines 127 to 138
// Wait for initialization with timeout
const maxRetries = 50; // 5 seconds total
let retries = 0;

await new Promise<void>((resolve, reject) => {
const checkInterval = setInterval(() => {
retries++;
if (retries > maxRetries) {
clearInterval(checkInterval);
reject(new Error("Worker initialization timeout"));
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot initializeWorker() の戻り値をPromiseにすればintervalは不要になると思います。

また、再初期化およびrestoreState中にrunCommand()などが呼ばれるのを防ぐためこの部分をmutex.current.runExclusive()で囲ってください。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdfa2a1. Changed initializeWorker() to return a Promise and wrapped the entire interrupt() function in mutex.current.runExclusive() to prevent concurrent calls.

Comment on lines 174 to 177
// If interrupted or worker was terminated, return appropriate message
if (isInterrupted.current) {
return [{ type: "error", message: "実行が中断されました" }];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot interrupt()内のrejectメッセージ「Worker interrupted」で伝わるので、ここの分岐は不要です。
isInterrupted変数も不要になります。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdfa2a1. Removed isInterrupted variable and the conditional check. Now all errors (including "Worker interrupted") are handled uniformly.

@@ -0,0 +1,137 @@
// JavaScript web worker
let jsOutput = [];
let executedCommands = []; // Store successfully executed commands for state recovery
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot interrupt()でworkerをterminateしたらこの変数も消えてしまうので、ここに保存しても意味ないです。runtime.tsx側に保存してください。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bdfa2a1. Moved executedCommands to runtime.tsx (stored as useRef). The runtime now passes commands to the worker in the restoreState message, so they persist across worker terminations.

…ime, use mutex for interrupt, remove isInterrupted

Co-authored-by: na-trium-144 <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 31, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • sparrow.cloudflare.com
    • Triggering command: /usr/local/bin/node --no-warnings --experimental-vm-modules /home/REDACTED/work/my-code/my-code/node_modules/wrangler/wrangler-dist/cli.js types --env-interface CloudflareEnv cloudflare-env.d.ts (dns block)
  • workers.cloudflare.com
    • Triggering command: /usr/local/bin/node --no-warnings --experimental-vm-modules /home/REDACTED/work/my-code/my-code/node_modules/wrangler/wrangler-dist/cli.js types --env-interface CloudflareEnv cloudflare-env.d.ts (dns block)
    • Triggering command: node /home/REDACTED/work/my-code/my-code/node_modules/.bin/next lint (dns block)
    • Triggering command: node /home/REDACTED/work/my-code/my-code/node_modules/.bin/next build (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@na-trium-144 na-trium-144 deleted the copilot/add-js-runtime-api branch November 12, 2025 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants